support-frontend/assets/pages/[countryGroupId]/components/thankYouComponent.tsx (400 lines of code) (raw):
import { css } from '@emotion/react';
import { storage } from '@guardian/libs';
import {
from,
neutral,
space,
sport,
until,
} from '@guardian/source/foundations';
import { Container, LinkButton } from '@guardian/source/react-components';
import { FooterWithContents } from '@guardian/source-development-kitchen/react-components';
import { Header } from 'components/headers/simpleHeader/simpleHeader';
import { PageScaffold } from 'components/page/pageScaffold';
import type { ThankYouModuleType } from 'components/thankYou/thankYouModule';
import { getThankYouModuleData } from 'components/thankYou/thankYouModuleData';
import type { Participations } from 'helpers/abTests/models';
import type { ContributionType } from 'helpers/contributions';
import { Country } from 'helpers/internationalisation/classes/country';
import type { ActiveProductKey } from 'helpers/productCatalog';
import type { ActivePaperProductOptions } from 'helpers/productPrice/productOptions';
import type { Promotion } from 'helpers/productPrice/promotions';
import { type CsrfState } from 'helpers/redux/checkout/csrf/state';
import type { UserType } from 'helpers/redux/checkout/personalDetails/state';
import { get } from 'helpers/storage/cookie';
import {
OPHAN_COMPONENT_ID_RETURN_TO_GUARDIAN,
OPHAN_COMPONENT_ID_RETURN_TO_OBSERVER,
} from 'helpers/thankYouPages/utils/ophan';
import { trackComponentClick } from 'helpers/tracking/behaviour';
import { successfulContributionConversion } from 'helpers/tracking/googleTagManager';
import {
sendEventCheckoutValue,
sendEventContributionCheckoutConversion,
sendEventOneTimeCheckoutValue,
} from 'helpers/tracking/quantumMetric';
import { getUser } from 'helpers/user/user';
import { formatUserDate } from 'helpers/utilities/dateConversions';
import { type GeoId, getGeoIdConfig } from 'pages/geoIdConfig';
import { ObserverPrint } from 'pages/paper-subscription-landing/helpers/products';
import ThankYouFooter from 'pages/supporter-plus-thank-you/components/thankYouFooter';
import ThankYouHeader from 'pages/supporter-plus-thank-you/components/thankYouHeader/thankYouHeader';
import { productDeliveryOrStartDate } from 'pages/weekly-subscription-checkout/helpers/deliveryDays';
import type { BenefitsCheckListData } from '../../../components/checkoutBenefits/benefitsCheckList';
import ThankYouModules from '../../../components/thankYou/thankyouModules';
import type { LandingPageVariant } from '../../../helpers/globalsAndSwitches/landingPageSettings';
import {
getReturnAddress,
getThankYouOrder,
} from '../checkout/helpers/sessionStorage';
const thankYouContainer = css`
position: relative;
${from.tablet} {
background-color: ${sport[800]};
}
`;
const headerContainer = css`
${from.desktop} {
width: 83%;
}
${from.leftCol} {
width: calc(75% - ${space[3]}px);
}
`;
const buttonContainer = css`
position: absolute;
bottom: ${space[8]}px;
& > a:first-child {
margin-right: ${space[3]}px;
}
${until.tablet} {
& > a {
margin-bottom: ${space[4]}px;
}
}
`;
const buttonColor = css`
background-color: ${neutral[100]};
${from.tablet} {
background-color: ${sport[800]};
}
`;
export type CheckoutComponentProps = {
geoId: GeoId;
csrf: CsrfState;
payment: {
originalAmount: number;
discountedAmount?: number;
contributionAmount?: number;
finalAmount: number;
};
productKey?: ActiveProductKey;
ratePlanKey?: string;
promotion?: Promotion;
identityUserType: UserType;
abParticipations: Participations;
landingPageSettings: LandingPageVariant;
};
export function ThankYouComponent({
geoId,
csrf,
payment,
productKey = 'Contribution',
ratePlanKey,
promotion,
identityUserType,
abParticipations,
landingPageSettings,
}: CheckoutComponentProps) {
const countryId = Country.fromString(get('GU_country') ?? 'GB') ?? 'GB';
const user = getUser();
const isSignedIn = user.isSignedIn;
const { countryGroupId, currencyKey } = getGeoIdConfig(geoId);
// Session storage order (from Checkout)
const order = getThankYouOrder();
if (!order) {
const sessionStorageOrder = storage.session.get('thankYouOrder');
return (
<div>Unable to read your order {JSON.stringify(sessionStorageOrder)}</div>
);
}
const isPending = order.status === 'pending';
/**
* contributionType is only applicable to SupporterPlus and Contributions.
* We should remove it for something more generic.
*/
const isTier =
productKey === 'Contribution' ||
productKey === 'SupporterPlus' ||
productKey === 'TierThree';
let contributionType: ContributionType;
switch (ratePlanKey) {
case 'Monthly':
case 'RestOfWorldMonthly':
case 'DomesticMonthly':
case 'RestOfWorldMonthlyV2':
case 'DomesticMonthlyV2':
case 'Everyday':
case 'Sixday':
case 'Weekend':
case 'Saturday':
case 'Sunday':
contributionType = 'MONTHLY';
break;
case 'Annual':
case 'RestOfWorldAnnual':
case 'DomesticAnnual':
case 'RestOfWorldAnnualV2':
case 'DomesticAnnualV2':
contributionType = 'ANNUAL';
break;
default:
// A one-off contribution indicated by the absence of product and ratePlan
contributionType = 'ONE_OFF';
break;
}
const isOneOff = contributionType === 'ONE_OFF';
// track conversion with GTM
const paymentMethod =
order.paymentMethod === 'StripeExpressCheckoutElement'
? 'Stripe'
: order.paymentMethod;
successfulContributionConversion(
payment.finalAmount, // This is the final amount after discounts
contributionType,
currencyKey,
paymentMethod,
productKey,
);
/**
* This is some annoying transformation we need from
* Product API => Contributions work we need to do
*/
const billingPeriod = contributionType === 'ANNUAL' ? 'Annual' : 'Monthly';
if (isOneOff) {
// track conversion with QM
sendEventOneTimeCheckoutValue(
payment.originalAmount, // This is the amount before discounts
currencyKey,
true,
);
} else {
// track conversion with QM
sendEventCheckoutValue(
payment.originalAmount, // This is the amount before discounts
productKey,
billingPeriod,
currencyKey,
true,
);
}
// track conversion with QM
sendEventContributionCheckoutConversion(
payment.originalAmount, // This is the amount before discounts
contributionType,
currencyKey,
);
const subscriptionCardProductsKey: ActiveProductKey[] = ['SubscriptionCard'];
const paperProductsKeys: ActiveProductKey[] = [
'NationalDelivery',
'HomeDelivery',
];
const printProductsKeys: ActiveProductKey[] = [
...subscriptionCardProductsKey,
...paperProductsKeys,
'GuardianWeeklyDomestic',
'GuardianWeeklyRestOfWorld',
];
const isPrint = printProductsKeys.includes(productKey);
const getObserver = (): ObserverPrint | undefined => {
if (paperProductsKeys.includes(productKey) && ratePlanKey === 'Sunday') {
return ObserverPrint.Paper;
}
if (
subscriptionCardProductsKey.includes(productKey) &&
ratePlanKey === 'Sunday'
) {
return ObserverPrint.SubscriptionCard;
}
return undefined;
};
const observerPrint = getObserver();
const isGuardianPrint =
printProductsKeys.includes(productKey) && ratePlanKey !== 'Sunday';
const isDigitalEdition = productKey === 'DigitalSubscription';
const isGuardianAdLite = productKey === 'GuardianAdLite';
const isOneOffPayPal = order.paymentMethod === 'PayPal' && isOneOff;
const isSupporterPlus = productKey === 'SupporterPlus';
const isTierThree = productKey === 'TierThree';
const validEmail = order.email !== '';
const showNewspaperArchiveBenefit = ['v1', 'v2', 'control'].includes(
abParticipations.newspaperArchiveBenefit ?? '',
);
// Clarify Guardian Ad-lite thankyou page states
const isNotRegistered = identityUserType === 'new';
const isRegisteredAndSignedIn = !isNotRegistered && isSignedIn;
const isRegisteredAndNotSignedIn = !isNotRegistered && !isSignedIn;
const getBenefits = (): BenefitsCheckListData[] => {
// Three Tier products get their config from the Landing Page tool
if (isTier) {
// Also show SupporterPlus benefits for TierThree
const tierThreeAdditionalBenefits =
productKey === 'TierThree'
? landingPageSettings.products.SupporterPlus.benefits.map(
(benefit) => ({
isChecked: true,
text: benefit.copy,
}),
)
: [];
return [
...landingPageSettings.products[productKey].benefits.map((benefit) => ({
isChecked: true,
text: benefit.copy,
})),
...tierThreeAdditionalBenefits,
];
}
return [];
};
const benefitsChecklist = getBenefits();
const deliveryDate = productDeliveryOrStartDate(
productKey,
ratePlanKey as ActivePaperProductOptions,
);
const startDate = deliveryDate ? formatUserDate(deliveryDate) : undefined;
const thankYouModuleData = getThankYouModuleData(
productKey,
countryGroupId,
countryId,
csrf,
isOneOff,
isSupporterPlus,
startDate,
undefined,
undefined,
isTierThree,
benefitsChecklist,
undefined,
undefined,
payment.finalAmount,
getReturnAddress(), // Session storage returnAddress (from GuardianAdLiteLanding)
isSignedIn,
observerPrint,
);
const maybeThankYouModule = (
condition: boolean,
moduleType: ThankYouModuleType,
): ThankYouModuleType[] => (condition ? [moduleType] : []);
const thankYouModules: ThankYouModuleType[] = [
...maybeThankYouModule(
!isPending && isNotRegistered && !isGuardianAdLite && !isGuardianPrint,
'signUp',
), // Complete your Guardian account
...maybeThankYouModule(
isRegisteredAndNotSignedIn && !isGuardianAdLite && !isGuardianPrint,
'signIn',
), // Sign in to access your benefits
...maybeThankYouModule(isTierThree, 'benefits'),
...maybeThankYouModule(
isTierThree && showNewspaperArchiveBenefit,
'newspaperArchiveBenefit',
),
...maybeThankYouModule(isTierThree || isGuardianPrint, 'subscriptionStart'),
...maybeThankYouModule(isTierThree || isSupporterPlus, 'appsDownload'),
...maybeThankYouModule(isOneOff && validEmail, 'supportReminder'),
...maybeThankYouModule(
isOneOff ||
(!(isTierThree && showNewspaperArchiveBenefit) &&
isSignedIn &&
!isGuardianAdLite &&
!observerPrint),
'feedback',
),
...maybeThankYouModule(isDigitalEdition, 'appDownloadEditions'),
...maybeThankYouModule(countryId === 'AU', 'ausMap'),
...maybeThankYouModule(
!isTierThree && !isGuardianAdLite && !isPrint,
'socialShare',
),
...maybeThankYouModule(isGuardianAdLite || !!observerPrint, 'whatNext'), // All
...maybeThankYouModule(
isGuardianAdLite && isRegisteredAndNotSignedIn,
'signInToActivate',
),
...maybeThankYouModule(
isGuardianAdLite && isRegisteredAndSignedIn,
'reminderToSignIn',
),
...maybeThankYouModule(
isGuardianAdLite && isNotRegistered,
'reminderToActivateSubscription',
),
...maybeThankYouModule(
isGuardianAdLite && (isRegisteredAndSignedIn || isNotRegistered),
'headlineReturn',
),
];
return (
<PageScaffold
header={<Header />}
footer={
<FooterWithContents>
{<ThankYouFooter observerPrint={observerPrint} />}
</FooterWithContents>
}
>
<div css={thankYouContainer}>
<Container>
<div css={headerContainer}>
<ThankYouHeader
isSignedIn={isSignedIn}
productKey={productKey}
ratePlanKey={ratePlanKey}
name={order.firstName}
amount={payment.originalAmount}
contributionType={contributionType}
amountIsAboveThreshold={isSupporterPlus}
isOneOffPayPal={isOneOffPayPal}
showDirectDebitMessage={order.paymentMethod === 'DirectDebit'}
currency={currencyKey}
promotion={promotion}
identityUserType={identityUserType}
observerPrint={observerPrint}
paymentStatus={order.status}
startDate={startDate}
/>
</div>
<ThankYouModules
isSignedIn={isSignedIn}
thankYouModules={thankYouModules}
thankYouModulesData={thankYouModuleData}
/>
<div css={buttonContainer}>
{!!observerPrint && (
<LinkButton
href="https://www.observer.co.uk/welcome"
priority="tertiary"
onClick={() =>
trackComponentClick(OPHAN_COMPONENT_ID_RETURN_TO_OBSERVER)
}
cssOverrides={buttonColor}
>
Return to the Observer
</LinkButton>
)}
{!isGuardianAdLite && (
<LinkButton
href="https://www.theguardian.com"
priority="tertiary"
onClick={() =>
trackComponentClick(OPHAN_COMPONENT_ID_RETURN_TO_GUARDIAN)
}
cssOverrides={buttonColor}
>
Return to the Guardian
</LinkButton>
)}
</div>
</Container>
</div>
</PageScaffold>
);
}